﻿using Facebook;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace Niknak.Social.Facebook
{
    /// <summary>
    /// Provides basic methods to retrieve comments from Facebook objects
    /// </summary>
    public static class Comments
    {
        /// <summary>
        /// Describes the types of Facebook cursors
        /// </summary>
        public enum CursorType
        {
            /// <summary>
            /// For the previous page
            /// </summary>
            Before,
            /// <summary>
            /// For the next page
            /// </summary>
            After
        }

        private const int DEFAULTPAGESIZE = 25;
        private const string GRAPHPATH = "/{0}/comments?limit={1}{2}";

        /// <summary>
        /// Gets a list of comments for the specified post, wrapped by a container object with additional information about the request page
        /// </summary>
        /// <param name="accessToken">Facebook access token</param>
        /// <param name="postId">ID of the post to get comments for</param>
        public static CommentsPage GetPostComments(string accessToken, string postId)
        {
            return GetPostComments(accessToken, postId, DEFAULTPAGESIZE, null, CursorType.Before);
        }

        /// <summary>
        /// Gets a list of comments for the specified post, wrapped by a container object with additional information about the request page
        /// </summary>
        /// <param name="accessToken">Facebook access token</param>
        /// <param name="postId">ID of the post to get comments for</param>
        /// <param name="pageSize">Limit the response to the specified page size</param>
        public static CommentsPage GetPostComments(string accessToken, string postId, int pageSize)
        {
            return GetPostComments(accessToken, postId, pageSize, null, CursorType.Before);
        }

        /// <summary>
        /// Gets a list of comments for the specified post, wrapped by a container object with additional information about the request page
        /// </summary>
        /// <param name="accessToken">Facebook access token</param>
        /// <param name="postId">ID of the post to get comments for</param>
        /// <param name="pageSize">Limit the response to the specified page size</param>
        /// <param name="cursor">Use the specified cursor ID from a previous page-limited request</param>
        /// <param name="cursorType">The type of cursor specified (before or after)</param>
        public static CommentsPage GetPostComments(string accessToken, string postId, int pageSize, string cursor, CursorType cursorType)
        {
            CommentsPage returnData = new CommentsPage()
            { 
                StartOfStream = true,
                EndOfStream = true 
            };

            List<Comment> comments = new List<Comment>();

            string requestPath = string.Format(
                GRAPHPATH,
                postId,
                pageSize,
                !string.IsNullOrEmpty(cursor) ? cursorType == CursorType.Before ? "&before=" + cursor.Replace("=", "%3D") : "&after=" + cursor.Replace("=", "%3D") : ""
            );

            FacebookClient fbClient = new FacebookClient(accessToken);
            JObject postComments = JObject.Parse(fbClient.Get(requestPath).ToString());

            if (postComments.Property("data") != null)
            {
                foreach (JObject comment in postComments.Value<JArray>("data"))
                    comments.Add(new Comment()
                        {
                            Id = comment.Value<string>("id"),
                            CreatedTime = comment.Value<DateTime>("created_time"),
                            UserId = comment.Value<JObject>("from").Value<string>("id"),
                            UserName = comment.Value<JObject>("from").Value<string>("name"),
                            LikeCount = comment.Value<long>("like_count"),
                            Message = comment.Value<string>("message")
                        });

                returnData.Data = comments.ToArray();
            }

            if (postComments.Property("paging") != null)
            {
                if (postComments.Value<JObject>("paging").Property("cursors") != null)
                {
                    returnData.BeforeCursor = postComments["paging"]["cursors"].Value<string>("before");
                    returnData.AfterCursor = postComments["paging"]["cursors"].Value<string>("after");
                }

                if (postComments.Value<JObject>("paging").Property("previous") != null)
                    returnData.StartOfStream = false;
                if (postComments.Value<JObject>("paging").Property("next") != null)
                    returnData.EndOfStream = false;
            }

            return returnData;
        }

        /// <summary>
        /// Wrapper for comment data
        /// </summary>
        public struct CommentsPage
        {
            /// <summary>
            /// Comments for the specified object
            /// </summary>
            public Comment[] Data;
            /// <summary>
            /// Cursor for previous results page - not for long term storage
            /// </summary>
            public string BeforeCursor;
            /// <summary>
            /// Cursor for next results page - not for long term storage
            /// </summary>
            public string AfterCursor;
            /// <summary>
            /// True when there are is no previous page
            /// </summary>
            public bool StartOfStream;
            /// <summary>
            /// True when there are is no next page
            /// </summary>
            public bool EndOfStream;
        }

        /// <summary>
        /// Data to describe a comment
        /// </summary>
        public struct Comment
        {
            /// <summary>
            /// The unique ID for the comment
            /// </summary>
            public string Id;
            /// <summary>
            /// Date and time of when the comment was created
            /// </summary>
            public DateTime CreatedTime;
            /// <summary>
            /// The ID of the user who created the comment
            /// </summary>
            public string UserId;
            /// <summary>
            /// The display name of the user who created the comment
            /// </summary>
            public string UserName;
            /// <summary>
            /// The number of linkes the comment has received
            /// </summary>
            public long LikeCount;
            /// <summary>
            /// The content of the comment
            /// </summary>
            public string Message;
        }
    }
}
